home *** CD-ROM | disk | FTP | other *** search
/ PC PowerPlay 58 / pcpp58a.iso / extras / quake 3 source / Q3A_ToolSource.exe / Main / brush.c < prev    next >
Encoding:
C/C++ Source or Header  |  2001-01-02  |  15.0 KB  |  834 lines

  1. #include "qbsp.h"
  2.  
  3.  
  4. int        c_active_brushes;
  5.  
  6. int        c_nodes;
  7.  
  8. // if a brush just barely pokes onto the other side,
  9. // let it slide by without chopping
  10. #define    PLANESIDE_EPSILON    0.001
  11. //0.1
  12.  
  13.  
  14.  
  15.  
  16. /*
  17. ================
  18. CountBrushList
  19. ================
  20. */
  21. int    CountBrushList (bspbrush_t *brushes)
  22. {
  23.     int    c;
  24.  
  25.     c = 0;
  26.     for ( ; brushes ; brushes = brushes->next)
  27.         c++;
  28.     return c;
  29. }
  30.  
  31.  
  32. /*
  33. ================
  34. AllocBrush
  35. ================
  36. */
  37. bspbrush_t *AllocBrush (int numsides)
  38. {
  39.     bspbrush_t    *bb;
  40.     int            c;
  41.  
  42.     c = (int)&(((bspbrush_t *)0)->sides[numsides]);
  43.     bb = malloc(c);
  44.     memset (bb, 0, c);
  45.     if (numthreads == 1)
  46.         c_active_brushes++;
  47.     return bb;
  48. }
  49.  
  50. /*
  51. ================
  52. FreeBrush
  53. ================
  54. */
  55. void FreeBrush (bspbrush_t *brushes)
  56. {
  57.     int            i;
  58.  
  59.     for (i=0 ; i<brushes->numsides ; i++)
  60.         if (brushes->sides[i].winding)
  61.             FreeWinding(brushes->sides[i].winding);
  62.     free (brushes);
  63.     if (numthreads == 1)
  64.         c_active_brushes--;
  65. }
  66.  
  67.  
  68. /*
  69. ================
  70. FreeBrushList
  71. ================
  72. */
  73. void FreeBrushList (bspbrush_t *brushes)
  74. {
  75.     bspbrush_t    *next;
  76.  
  77.     for ( ; brushes ; brushes = next)
  78.     {
  79.         next = brushes->next;
  80.  
  81.         FreeBrush (brushes);
  82.     }        
  83. }
  84.  
  85. /*
  86. ==================
  87. CopyBrush
  88.  
  89. Duplicates the brush, the sides, and the windings
  90. ==================
  91. */
  92. bspbrush_t *CopyBrush (bspbrush_t *brush)
  93. {
  94.     bspbrush_t *newbrush;
  95.     int            size;
  96.     int            i;
  97.     
  98.     size = (int)&(((bspbrush_t *)0)->sides[brush->numsides]);
  99.  
  100.     newbrush = AllocBrush (brush->numsides);
  101.     memcpy (newbrush, brush, size);
  102.  
  103.     for (i=0 ; i<brush->numsides ; i++)
  104.     {
  105.         if (brush->sides[i].winding)
  106.             newbrush->sides[i].winding = CopyWinding (brush->sides[i].winding);
  107.     }
  108.  
  109.     return newbrush;
  110. }
  111.  
  112.  
  113. /*
  114. ================
  115. DrawBrushList
  116. ================
  117. */
  118. void DrawBrushList (bspbrush_t *brush)
  119. {
  120.     int        i;
  121.     side_t    *s;
  122.  
  123.     GLS_BeginScene ();
  124.     for ( ; brush ; brush=brush->next)
  125.     {
  126.         for (i=0 ; i<brush->numsides ; i++)
  127.         {
  128.             s = &brush->sides[i];
  129.             if (!s->winding)
  130.                 continue;
  131.             GLS_Winding (s->winding, 0);
  132.         }
  133.     }
  134.     GLS_EndScene ();
  135. }
  136.  
  137.  
  138.  
  139. /*
  140. ================
  141. WriteBrushList
  142. ================
  143. */
  144. void WriteBrushList (char *name, bspbrush_t *brush, qboolean onlyvis)
  145. {
  146.     int        i;
  147.     side_t    *s;
  148.     FILE    *f;
  149.  
  150.     qprintf ("writing %s\n", name);
  151.     f = SafeOpenWrite (name);
  152.  
  153.     for ( ; brush ; brush=brush->next)
  154.     {
  155.         for (i=0 ; i<brush->numsides ; i++)
  156.         {
  157.             s = &brush->sides[i];
  158.             if (!s->winding)
  159.                 continue;
  160.             if (onlyvis && !s->visible)
  161.                 continue;
  162.             OutputWinding (brush->sides[i].winding, f);
  163.         }
  164.     }
  165.  
  166.     fclose (f);
  167. }
  168.  
  169.  
  170. /*
  171. =============
  172. PrintBrush
  173. =============
  174. */
  175. void PrintBrush (bspbrush_t *brush)
  176. {
  177.     int        i;
  178.  
  179.     _printf ("brush: %p\n", brush);
  180.     for (i=0;i<brush->numsides ; i++)
  181.     {
  182.         pw(brush->sides[i].winding);
  183.         _printf ("\n");
  184.     }
  185. }
  186.  
  187. /*
  188. ==================
  189. BoundBrush
  190.  
  191. Sets the mins/maxs based on the windings
  192. returns false if the brush doesn't enclose a valid volume
  193. ==================
  194. */
  195. qboolean BoundBrush (bspbrush_t *brush)
  196. {
  197.     int            i, j;
  198.     winding_t    *w;
  199.  
  200.     ClearBounds (brush->mins, brush->maxs);
  201.     for (i=0 ; i<brush->numsides ; i++)
  202.     {
  203.         w = brush->sides[i].winding;
  204.         if (!w)
  205.             continue;
  206.         for (j=0 ; j<w->numpoints ; j++)
  207.             AddPointToBounds (w->p[j], brush->mins, brush->maxs);
  208.     }
  209.  
  210.     for (i=0 ; i<3 ; i++) {
  211.         if (brush->mins[i] < MIN_WORLD_COORD || brush->maxs[i] > MAX_WORLD_COORD
  212.             || brush->mins[i] >= brush->maxs[i] ) {
  213.             return qfalse;
  214.         }
  215.     }
  216.  
  217.     return qtrue;
  218. }
  219.  
  220. /*
  221. ==================
  222. CreateBrushWindings
  223.  
  224. makes basewindigs for sides and mins / maxs for the brush
  225. returns false if the brush doesn't enclose a valid volume
  226. ==================
  227. */
  228. qboolean CreateBrushWindings (bspbrush_t *brush)
  229. {
  230.     int            i, j;
  231.     winding_t    *w;
  232.     side_t        *side;
  233.     plane_t        *plane;
  234.  
  235.     for (i=0 ; i<brush->numsides ; i++)
  236.     {
  237.         side = &brush->sides[i];
  238.         plane = &mapplanes[side->planenum];
  239.         w = BaseWindingForPlane (plane->normal, plane->dist);
  240.         for (j=0 ; j<brush->numsides && w; j++)
  241.         {
  242.             if (i == j)
  243.                 continue;
  244.             if ( brush->sides[j].planenum == ( brush->sides[i].planenum ^ 1 ) )
  245.                 continue;        // back side clipaway
  246.             if (brush->sides[j].bevel)
  247.                 continue;
  248.             if (brush->sides[j].backSide)
  249.                 continue;
  250.             plane = &mapplanes[brush->sides[j].planenum^1];
  251.             ChopWindingInPlace (&w, plane->normal, plane->dist, 0); //CLIP_EPSILON);
  252.         }
  253.  
  254.         side->winding = w;
  255.     }
  256.  
  257.     return BoundBrush (brush);
  258. }
  259.  
  260. /*
  261. ==================
  262. BrushFromBounds
  263.  
  264. Creates a new axial brush
  265. ==================
  266. */
  267. bspbrush_t    *BrushFromBounds (vec3_t mins, vec3_t maxs)
  268. {
  269.     bspbrush_t    *b;
  270.     int            i;
  271.     vec3_t        normal;
  272.     vec_t        dist;
  273.  
  274.     b = AllocBrush (6);
  275.     b->numsides = 6;
  276.     for (i=0 ; i<3 ; i++)
  277.     {
  278.         VectorClear (normal);
  279.         normal[i] = 1;
  280.         dist = maxs[i];
  281.         b->sides[i].planenum = FindFloatPlane (normal, dist);
  282.  
  283.         normal[i] = -1;
  284.         dist = -mins[i];
  285.         b->sides[3+i].planenum = FindFloatPlane (normal, dist);
  286.     }
  287.  
  288.     CreateBrushWindings (b);
  289.  
  290.     return b;
  291. }
  292.  
  293. /*
  294. ==================
  295. BrushVolume
  296.  
  297. ==================
  298. */
  299. vec_t BrushVolume (bspbrush_t *brush)
  300. {
  301.     int            i;
  302.     winding_t    *w;
  303.     vec3_t        corner;
  304.     vec_t        d, area, volume;
  305.     plane_t        *plane;
  306.  
  307.     if (!brush)
  308.         return 0;
  309.  
  310.     // grab the first valid point as the corner
  311.  
  312.     w = NULL;
  313.     for (i=0 ; i<brush->numsides ; i++)
  314.     {
  315.         w = brush->sides[i].winding;
  316.         if (w)
  317.             break;
  318.     }
  319.     if (!w)
  320.         return 0;
  321.     VectorCopy (w->p[0], corner);
  322.  
  323.     // make tetrahedrons to all other faces
  324.  
  325.     volume = 0;
  326.     for ( ; i<brush->numsides ; i++)
  327.     {
  328.         w = brush->sides[i].winding;
  329.         if (!w)
  330.             continue;
  331.         plane = &mapplanes[brush->sides[i].planenum];
  332.         d = -(DotProduct (corner, plane->normal) - plane->dist);
  333.         area = WindingArea (w);
  334.         volume += d*area;
  335.     }
  336.  
  337.     volume /= 3;
  338.     return volume;
  339. }
  340.  
  341.  
  342. /*
  343. ==================
  344. WriteBspBrushMap
  345. ==================
  346. */
  347. void WriteBspBrushMap (char *name, bspbrush_t *list)
  348. {
  349.     FILE    *f;
  350.     side_t    *s;
  351.     int        i;
  352.     winding_t    *w;
  353.  
  354.     _printf ("writing %s\n", name);
  355.     f = fopen (name, "wb");
  356.     if (!f)
  357.         Error ("Can't write %s\b", name);
  358.  
  359.     fprintf (f, "{\n\"classname\" \"worldspawn\"\n");
  360.  
  361.     for ( ; list ; list=list->next )
  362.     {
  363.         fprintf (f, "{\n");
  364.         for (i=0,s=list->sides ; i<list->numsides ; i++,s++)
  365.         {
  366.             w = BaseWindingForPlane (mapplanes[s->planenum].normal, mapplanes[s->planenum].dist);
  367.  
  368.             fprintf (f,"( %i %i %i ) ", (int)w->p[0][0], (int)w->p[0][1], (int)w->p[0][2]);
  369.             fprintf (f,"( %i %i %i ) ", (int)w->p[1][0], (int)w->p[1][1], (int)w->p[1][2]);
  370.             fprintf (f,"( %i %i %i ) ", (int)w->p[2][0], (int)w->p[2][1], (int)w->p[2][2]);
  371.  
  372.             fprintf (f, "notexture 0 0 0 1 1\n" );
  373.             FreeWinding (w);
  374.         }
  375.         fprintf (f, "}\n");
  376.     }
  377.     fprintf (f, "}\n");
  378.  
  379.     fclose (f);
  380.  
  381. }
  382.  
  383.  
  384. //=====================================================================================
  385.  
  386. /*
  387. ====================
  388. FilterBrushIntoTree_r
  389.  
  390. ====================
  391. */
  392. int FilterBrushIntoTree_r( bspbrush_t *b, node_t *node ) {
  393.     bspbrush_t        *front, *back;
  394.     int                c;
  395.  
  396.     if ( !b ) {
  397.         return 0;
  398.     }
  399.  
  400.     // add it to the leaf list
  401.     if ( node->planenum == PLANENUM_LEAF ) {
  402.         b->next = node->brushlist;
  403.         node->brushlist = b;
  404.  
  405.         // classify the leaf by the structural brush
  406.         if ( !b->detail ) {
  407.             if ( b->opaque ) {
  408.                 node->opaque = qtrue;
  409.                 node->areaportal = qfalse;
  410.             } else if ( b->contents & CONTENTS_AREAPORTAL ) {
  411.                 if ( !node->opaque ) {
  412.                     node->areaportal = qtrue;
  413.                 }
  414.             }
  415.         }
  416.  
  417.         return 1;
  418.     }
  419.  
  420.     // split it by the node plane
  421.     SplitBrush ( b, node->planenum, &front, &back );
  422.     FreeBrush( b );
  423.  
  424.     c = 0;
  425.     c += FilterBrushIntoTree_r( front, node->children[0] );
  426.     c += FilterBrushIntoTree_r( back, node->children[1] );
  427.  
  428.     return c;
  429. }
  430.  
  431. /*
  432. =====================
  433. FilterDetailBrushesIntoTree
  434.  
  435. Fragment all the detail brushes into the structural leafs
  436. =====================
  437. */
  438. void FilterDetailBrushesIntoTree( entity_t *e, tree_t *tree ) {
  439.     bspbrush_t            *b, *newb;
  440.     int                    r;
  441.     int                    c_unique, c_clusters;
  442.     int                    i;
  443.  
  444.     qprintf( "----- FilterDetailBrushesIntoTree -----\n");
  445.  
  446.     c_unique = 0;
  447.     c_clusters = 0;
  448.     for ( b = e->brushes ; b ; b = b->next ) {
  449.         if ( !b->detail ) {
  450.             continue;
  451.         }
  452.         c_unique++;
  453.         newb = CopyBrush( b );
  454.         r = FilterBrushIntoTree_r( newb, tree->headnode );
  455.         c_clusters += r;
  456.  
  457.         // mark all sides as visible so drawsurfs are created
  458.         if ( r ) {
  459.             for ( i = 0 ; i < b->numsides ; i++ ) {
  460.                 if ( b->sides[i].winding ) {
  461.                     b->sides[i].visible = qtrue;
  462.                 }
  463.             }
  464.         }
  465.     }
  466.  
  467.     qprintf( "%5i detail brushes\n", c_unique );
  468.     qprintf( "%5i cluster references\n", c_clusters );
  469. }
  470.  
  471. /*
  472. =====================
  473. FilterStructuralBrushesIntoTree
  474.  
  475. Mark the leafs as opaque and areaportals
  476. =====================
  477. */
  478. void FilterStructuralBrushesIntoTree( entity_t *e, tree_t *tree ) {
  479.     bspbrush_t            *b, *newb;
  480.     int                    r;
  481.     int                    c_unique, c_clusters;
  482.     int                    i;
  483.  
  484.     qprintf( "----- FilterStructuralBrushesIntoTree -----\n");
  485.  
  486.     c_unique = 0;
  487.     c_clusters = 0;
  488.     for ( b = e->brushes ; b ; b = b->next ) {
  489.         if ( b->detail ) {
  490.             continue;
  491.         }
  492.         c_unique++;
  493.         newb = CopyBrush( b );
  494.         r = FilterBrushIntoTree_r( newb, tree->headnode );
  495.         c_clusters += r;
  496.  
  497.         // mark all sides as visible so drawsurfs are created
  498.         if ( r ) {
  499.             for ( i = 0 ; i < b->numsides ; i++ ) {
  500.                 if ( b->sides[i].winding ) {
  501.                     b->sides[i].visible = qtrue;
  502.                 }
  503.             }
  504.         }
  505.     }
  506.  
  507.     qprintf( "%5i structural brushes\n", c_unique );
  508.     qprintf( "%5i cluster references\n", c_clusters );
  509. }
  510.  
  511.  
  512.  
  513. /*
  514. ================
  515. AllocTree
  516. ================
  517. */
  518. tree_t *AllocTree (void)
  519. {
  520.     tree_t    *tree;
  521.  
  522.     tree = malloc(sizeof(*tree));
  523.     memset (tree, 0, sizeof(*tree));
  524.     ClearBounds (tree->mins, tree->maxs);
  525.  
  526.     return tree;
  527. }
  528.  
  529. /*
  530. ================
  531. AllocNode
  532. ================
  533. */
  534. node_t *AllocNode (void)
  535. {
  536.     node_t    *node;
  537.  
  538.     node = malloc(sizeof(*node));
  539.     memset (node, 0, sizeof(*node));
  540.  
  541.     return node;
  542. }
  543.  
  544.  
  545. /*
  546. ================
  547. WindingIsTiny
  548.  
  549. Returns true if the winding would be crunched out of
  550. existance by the vertex snapping.
  551. ================
  552. */
  553. #define    EDGE_LENGTH    0.2
  554. qboolean WindingIsTiny (winding_t *w)
  555. {
  556. /*
  557.     if (WindingArea (w) < 1)
  558.         return qtrue;
  559.     return qfalse;
  560. */
  561.     int        i, j;
  562.     vec_t    len;
  563.     vec3_t    delta;
  564.     int        edges;
  565.  
  566.     edges = 0;
  567.     for (i=0 ; i<w->numpoints ; i++)
  568.     {
  569.         j = i == w->numpoints - 1 ? 0 : i+1;
  570.         VectorSubtract (w->p[j], w->p[i], delta);
  571.         len = VectorLength (delta);
  572.         if (len > EDGE_LENGTH)
  573.         {
  574.             if (++edges == 3)
  575.                 return qfalse;
  576.         }
  577.     }
  578.     return qtrue;
  579. }
  580.  
  581. /*
  582. ================
  583. WindingIsHuge
  584.  
  585. Returns true if the winding still has one of the points
  586. from basewinding for plane
  587. ================
  588. */
  589. qboolean WindingIsHuge (winding_t *w)
  590. {
  591.     int        i, j;
  592.  
  593.     for (i=0 ; i<w->numpoints ; i++)
  594.     {
  595.         for (j=0 ; j<3 ; j++)
  596.             if (w->p[i][j] <= MIN_WORLD_COORD || w->p[i][j] >= MAX_WORLD_COORD)
  597.                 return qtrue;
  598.     }
  599.     return qfalse;
  600. }
  601.  
  602. //============================================================
  603.  
  604. /*
  605. ==================
  606. BrushMostlyOnSide
  607.  
  608. ==================
  609. */
  610. int BrushMostlyOnSide (bspbrush_t *brush, plane_t *plane)
  611. {
  612.     int            i, j;
  613.     winding_t    *w;
  614.     vec_t        d, max;
  615.     int            side;
  616.  
  617.     max = 0;
  618.     side = PSIDE_FRONT;
  619.     for (i=0 ; i<brush->numsides ; i++)
  620.     {
  621.         w = brush->sides[i].winding;
  622.         if (!w)
  623.             continue;
  624.         for (j=0 ; j<w->numpoints ; j++)
  625.         {
  626.             d = DotProduct (w->p[j], plane->normal) - plane->dist;
  627.             if (d > max)
  628.             {
  629.                 max = d;
  630.                 side = PSIDE_FRONT;
  631.             }
  632.             if (-d > max)
  633.             {
  634.                 max = -d;
  635.                 side = PSIDE_BACK;
  636.             }
  637.         }
  638.     }
  639.     return side;
  640. }
  641.  
  642. /*
  643. ================
  644. SplitBrush
  645.  
  646. Generates two new brushes, leaving the original
  647. unchanged
  648. ================
  649. */
  650. void SplitBrush (bspbrush_t *brush, int planenum,
  651.     bspbrush_t **front, bspbrush_t **back)
  652. {
  653.     bspbrush_t    *b[2];
  654.     int            i, j;
  655.     winding_t    *w, *cw[2], *midwinding;
  656.     plane_t        *plane, *plane2;
  657.     side_t        *s, *cs;
  658.     float        d, d_front, d_back;
  659.  
  660.     *front = *back = NULL;
  661.     plane = &mapplanes[planenum];
  662.  
  663.     // check all points
  664.     d_front = d_back = 0;
  665.     for (i=0 ; i<brush->numsides ; i++)
  666.     {
  667.         w = brush->sides[i].winding;
  668.         if (!w)
  669.             continue;
  670.         for (j=0 ; j<w->numpoints ; j++)
  671.         {
  672.             d = DotProduct (w->p[j], plane->normal) - plane->dist;
  673.             if (d > 0 && d > d_front)
  674.                 d_front = d;
  675.             if (d < 0 && d < d_back)
  676.                 d_back = d;
  677.         }
  678.     }
  679.     if (d_front < 0.1) // PLANESIDE_EPSILON)
  680.     {    // only on back
  681.         *back = CopyBrush (brush);
  682.         return;
  683.     }
  684.     if (d_back > -0.1) // PLANESIDE_EPSILON)
  685.     {    // only on front
  686.         *front = CopyBrush (brush);
  687.         return;
  688.     }
  689.  
  690.     // create a new winding from the split plane
  691.  
  692.     w = BaseWindingForPlane (plane->normal, plane->dist);
  693.     for (i=0 ; i<brush->numsides && w ; i++)
  694.     {
  695.         if ( brush->sides[i].backSide ) {
  696.             continue;    // fake back-sided polygons never split
  697.         }
  698.         plane2 = &mapplanes[brush->sides[i].planenum ^ 1];
  699.         ChopWindingInPlace (&w, plane2->normal, plane2->dist, 0); // PLANESIDE_EPSILON);
  700.     }
  701.  
  702.     if (!w || WindingIsTiny (w) )
  703.     {    // the brush isn't really split
  704.         int        side;
  705.  
  706.         side = BrushMostlyOnSide (brush, plane);
  707.         if (side == PSIDE_FRONT)
  708.             *front = CopyBrush (brush);
  709.         if (side == PSIDE_BACK)
  710.             *back = CopyBrush (brush);
  711.         return;
  712.     }
  713.  
  714.     if (WindingIsHuge (w))
  715.     {
  716.         qprintf ("WARNING: huge winding\n");
  717.     }
  718.  
  719.     midwinding = w;
  720.  
  721.     // split it for real
  722.  
  723.     for (i=0 ; i<2 ; i++)
  724.     {
  725.         b[i] = AllocBrush (brush->numsides+1);
  726.         memcpy( b[i], brush, sizeof( bspbrush_t ) - sizeof( brush->sides ) );
  727.         b[i]->numsides = 0;
  728.         b[i]->next = NULL;
  729.         b[i]->original = brush->original;
  730.     }
  731.  
  732.     // split all the current windings
  733.  
  734.     for (i=0 ; i<brush->numsides ; i++)
  735.     {
  736.         s = &brush->sides[i];
  737.         w = s->winding;
  738.         if (!w)
  739.             continue;
  740.         ClipWindingEpsilon (w, plane->normal, plane->dist,
  741.             0 /*PLANESIDE_EPSILON*/, &cw[0], &cw[1]);
  742.         for (j=0 ; j<2 ; j++)
  743.         {
  744.             if (!cw[j])
  745.                 continue;
  746. /*
  747.             if (WindingIsTiny (cw[j]))
  748.             {
  749.                 FreeWinding (cw[j]);
  750.                 continue;
  751.             }
  752. */
  753.             cs = &b[j]->sides[b[j]->numsides];
  754.             b[j]->numsides++;
  755.             *cs = *s;
  756.             cs->winding = cw[j];
  757.         }
  758.     }
  759.  
  760.  
  761.     // see if we have valid polygons on both sides
  762.  
  763.     for (i=0 ; i<2 ; i++)
  764.     {
  765.         BoundBrush (b[i]);
  766.         for (j=0 ; j<3 ; j++)
  767.         {
  768.             if (b[i]->mins[j] < MIN_WORLD_COORD || b[i]->maxs[j] > MAX_WORLD_COORD)
  769.             {
  770.                 qprintf ("bogus brush after clip\n");
  771.                 break;
  772.             }
  773.         }
  774.  
  775.         if (b[i]->numsides < 3 || j < 3)
  776.         {
  777.             FreeBrush (b[i]);
  778.             b[i] = NULL;
  779.         }
  780.     }
  781.  
  782.     if ( !(b[0] && b[1]) )
  783.     {
  784.         if (!b[0] && !b[1])
  785.             qprintf ("split removed brush\n");
  786.         else
  787.             qprintf ("split not on both sides\n");
  788.         if (b[0])
  789.         {
  790.             FreeBrush (b[0]);
  791.             *front = CopyBrush (brush);
  792.         }
  793.         if (b[1])
  794.         {
  795.             FreeBrush (b[1]);
  796.             *back = CopyBrush (brush);
  797.         }
  798.         return;
  799.     }
  800.  
  801.     // add the midwinding to both sides
  802.     for (i=0 ; i<2 ; i++)
  803.     {
  804.         cs = &b[i]->sides[b[i]->numsides];
  805.         b[i]->numsides++;
  806.  
  807.         cs->planenum = planenum^i^1;
  808.         cs->shaderInfo = NULL;
  809.         if (i==0)
  810.             cs->winding = CopyWinding (midwinding);
  811.         else
  812.             cs->winding = midwinding;
  813.     }
  814.  
  815. {
  816.     vec_t    v1;
  817.     int        i;
  818.  
  819.     for (i=0 ; i<2 ; i++)
  820.     {
  821.         v1 = BrushVolume (b[i]);
  822.         if (v1 < 1.0)
  823.         {
  824.             FreeBrush (b[i]);
  825.             b[i] = NULL;
  826. //            qprintf ("tiny volume after clip\n");
  827.         }
  828.     }
  829. }
  830.  
  831.     *front = b[0];
  832.     *back = b[1];
  833. }
  834.